package com.note.app.service.impl;

import com.note.app.dto.*;
import com.note.app.mapper.UserMapper;
import com.note.app.mapper.UserTokenMapper;
import com.note.app.mapper.VerificationCodeMapper;
import com.note.app.service.UserService;
import com.note.app.utils.BeanConverterUtils;
import com.note.common.domain.User;
import com.note.common.domain.UserToken;
import com.note.common.domain.VerificationCode;
import com.note.common.exception.BusinessException;
import com.note.common.service.JiGuangSmsService;
import com.note.common.utils.JwtUtils;
import com.note.common.utils.RedisUtils;
import com.note.common.vo.ResponseVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.Objects;
import java.util.Random;

/**
 * 用户服务实现类
 * 实现用户相关的业务逻辑
 */
@Slf4j
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private BCryptPasswordEncoder passwordEncoder;

    @Autowired
    private UserTokenMapper userTokenMapper;

    @Autowired
    private VerificationCodeMapper verificationCodeMapper;

    @Autowired
    private JiGuangSmsService jiGuangSmsService;

    @Value("${jwt.expiration:86400}")
    private long jwtExpiration;

    private static final String TOKEN_PREFIX = "Bearer ";

    /**
     * 用户注册
     *
     * @param registerDTO 注册信息
     * @return 注册结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseVo<Void> register(UserRegisterDTO registerDTO) {
        // 1. 验证两次密码是否一致
        if (!Objects.equals(registerDTO.getPassword(), registerDTO.getConfirmPassword())) {
            return ResponseVo.paramError("两次密码不一致");
        }

        // 2. 验证用户名是否已存在
        User existUser = userMapper.findByUsername(registerDTO.getUsername());
        if (existUser != null) {
            return ResponseVo.paramError("用户名已被使用");
        }

        // 3. 验证邮箱是否已存在
        existUser = userMapper.findByEmail(registerDTO.getEmail());
        if (existUser != null) {
            return ResponseVo.paramError("邮箱已被使用");
        }

        // 4. 创建用户实体
        User user = new User();
        BeanUtils.copyProperties(registerDTO, user);

        // 5. 对密码进行加密
        user.setPassword(passwordEncoder.encode(registerDTO.getPassword()));

        // 6. 设置默认值
        if (!StringUtils.hasText(user.getNickname())) {
            user.setNickname(user.getUsername());
        }
        // 头像设置为null，不使用默认值
        user.setAvatar(null);
        user.setStatus(1); // 默认启用状态
        user.setCreatedAt(LocalDateTime.now());
        user.setUpdatedAt(LocalDateTime.now());

        // 7. 保存用户信息
        userMapper.insert(user);

        log.info("用户注册成功: userId={}, username={}", user.getId(), user.getUsername());
        return ResponseVo.success("注册成功");
    }

    /**
     * 手机号验证码注册
     *
     * @param registerDTO 手机注册信息
     * @return 注册结果，包含用户信息和token
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseVo<UserDTO> mobileRegister(MobileRegisterDTO registerDTO) {
        // 1. 验证两次密码是否一致
        if (!Objects.equals(registerDTO.getPassword(), registerDTO.getConfirmPassword())) {
            return ResponseVo.paramError("两次密码不一致");
        }

        // 2. 验证验证码
        String mobile = registerDTO.getMobile();
        String code = registerDTO.getCode();

        // 先尝试从Redis获取msgId（更快）
        String msgId = redisUtils.getMsgId(mobile, "REGISTER");
        boolean codeValid = false;

        if (msgId != null) {
            // 使用Redis中的msgId验证
            codeValid = jiGuangSmsService.verifyCode(msgId, code);
            log.info("使用Redis存储的msgId验证结果: {}", codeValid);

            if (codeValid) {
                // 验证成功后删除Redis中的msgId
                redisUtils.deleteMsgId(mobile, "REGISTER");
            }
        } else {
            // Redis中不存在，从数据库查询
            VerificationCode verificationCode = verificationCodeMapper.findLatestValidCode(
                    mobile, "REGISTER", LocalDateTime.now());

            if (verificationCode != null && verificationCode.getMsgId() != null) {
                // 通过极光API验证验证码
                codeValid = jiGuangSmsService.verifyCode(verificationCode.getMsgId(), code);
                log.info("通过数据库获取的msgId验证结果: {}", codeValid);

                // 验证成功，标记为已使用
                if (codeValid) {
                    verificationCodeMapper.markAsUsed(verificationCode.getId());
                }
            }
        }

        if (!codeValid) {
            return ResponseVo.paramError("验证码错误或已过期");
        }

        // 3. 检查手机号是否已注册
        User existUser = userMapper.findByMobile(mobile);
        if (existUser != null) {
            return ResponseVo.paramError("该手机号已注册");
        }

        // 4. 创建用户实体
        User user = new User();
        user.setMobile(mobile);

        // 设置用户名
        if (StringUtils.hasText(registerDTO.getUsername())) {
            // 验证用户名是否已存在
            existUser = userMapper.findByUsername(registerDTO.getUsername());
            if (existUser != null) {
                return ResponseVo.paramError("用户名已被使用");
            }
            user.setUsername(registerDTO.getUsername());
        } else {
            // 默认用户名
            user.setUsername("user_" + mobile);
        }

        // 设置昵称
        if (StringUtils.hasText(registerDTO.getNickname())) {
            user.setNickname(registerDTO.getNickname());
        } else {
            // 默认昵称
            user.setNickname("用户" + mobile.substring(7)); // 使用手机号后4位
        }

        // 使用手机号生成临时邮箱
        user.setEmail(mobile + "@noteapp.com");

        // 对密码进行加密
        user.setPassword(passwordEncoder.encode(registerDTO.getPassword()));

        // 头像设置为null
        user.setAvatar(null);
        user.setStatus(1); // 默认启用状态
        user.setCreatedAt(LocalDateTime.now());
        user.setUpdatedAt(LocalDateTime.now());

        // 5. 保存用户信息
        userMapper.insert(user);

        // 6. 生成JWT令牌
        String token = jwtUtils.generateToken(user.getId(), user.getUsername());
        if (token == null) {
            log.error("生成JWT令牌失败: userId={}, mobile={}", user.getId(), mobile);
            throw new BusinessException("注册失败，请稍后再试");
        }

        // 7. 保存令牌到数据库
        saveUserToken(user.getId(), token, registerDTO.getDeviceType(),
                registerDTO.getDeviceInfo(), registerDTO.getLoginIp());

        // 8. 缓存用户令牌到Redis
        redisUtils.setUserToken(user.getId(), token);

        // 9. 组装用户信息
        UserDTO userDTO = BeanConverterUtils.convertToUserDTO(user, TOKEN_PREFIX + token);

        log.info("手机号注册成功: userId={}, mobile={}", user.getId(), mobile);
        return ResponseVo.success("注册成功", userDTO);
    }

    /**
     * 用户登录
     *
     * @param loginDTO 登录信息
     * @return 登录结果，包含用户信息和token
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseVo<UserDTO> login(UserLoginDTO loginDTO) {
        // 1. 根据用户名查询用户
        User user = userMapper.findByUsername(loginDTO.getUsername());
        if (user == null) {
            return ResponseVo.paramError("用户名或密码错误");
        }

        // 2. 验证用户状态
        if (user.getStatus() != 1) {
            return ResponseVo.paramError("账号已被禁用");
        }

        // 3. 验证密码
        if (!passwordEncoder.matches(loginDTO.getPassword(), user.getPassword())) {
            return ResponseVo.paramError("用户名或密码错误");
        }

        // 4. 生成JWT令牌
        String token = jwtUtils.generateToken(user.getId(), user.getUsername());
        if (token == null) {
            log.error("生成JWT令牌失败: userId={}, username={}", user.getId(), user.getUsername());
            throw new BusinessException("登录失败，请稍后再试");
        }

        // 5. 保存令牌到数据库
        saveUserToken(user.getId(), token, loginDTO.getDeviceType(), loginDTO.getDeviceInfo(), loginDTO.getLoginIp());

        // 6. 缓存用户令牌到Redis（仍然保留Redis缓存提高性能）
        redisUtils.setUserToken(user.getId(), token);

        // 7. 组装用户信息
        UserDTO userDTO = BeanConverterUtils.convertToUserDTO(user, TOKEN_PREFIX + token);

        log.info("用户登录成功: userId={}, username={}", user.getId(), user.getUsername());
        return ResponseVo.success("登录成功", userDTO);
    }

    /**
     * 手机号验证码登录
     *
     * @param mobileLoginDTO 手机登录信息
     * @return 登录结果，包含用户信息和token
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseVo<UserDTO> mobileLogin(MobileLoginDTO mobileLoginDTO) {
        // 1. 验证验证码
        String mobile = mobileLoginDTO.getMobile();
        String code = mobileLoginDTO.getCode();

        // 先查询最近的一条验证码记录
        LocalDateTime now = LocalDateTime.now();

        // 先尝试从Redis获取msgId（更快）
        String msgId = redisUtils.getMsgId(mobile, "LOGIN");
        boolean codeValid = false;

        if (msgId != null) {
            // 使用Redis中的msgId验证
            codeValid = jiGuangSmsService.verifyCode(msgId, code);
            log.info("使用Redis存储的msgId验证结果: {}", codeValid);

            if (codeValid) {
                // 验证成功后删除Redis中的msgId
                redisUtils.deleteMsgId(mobile, "LOGIN");
            }
        } else {
            // Redis中不存在，从数据库查询
            VerificationCode verificationCode = verificationCodeMapper.findLatestValidCode(
                    mobile, "LOGIN", now);

            if (verificationCode != null && verificationCode.getMsgId() != null) {
                // 通过极光API验证验证码
                codeValid = jiGuangSmsService.verifyCode(verificationCode.getMsgId(), code);
                log.info("通过数据库获取的msgId验证结果: {}", codeValid);

                // 验证成功，标记为已使用
                if (codeValid) {
                    verificationCodeMapper.markAsUsed(verificationCode.getId());
                }
            }
        }

        if (!codeValid) {
            return ResponseVo.paramError("验证码错误或已过期");
        }

        // 2. 查询手机号是否已注册
        User user = userMapper.findByMobile(mobile);

        // 3. 手机号未注册，自动注册新用户
        if (user == null) {
            user = new User();
            user.setMobile(mobile);
            user.setUsername("user_" + mobile);
            user.setNickname("用户" + mobile.substring(7)); // 使用手机号后4位作为昵称
            user.setEmail(mobile + "@noteapp.com"); // 生成临时邮箱

            // 生成随机密码，用户可以后续修改
            String randomPassword = generateRandomPassword();
            user.setPassword(passwordEncoder.encode(randomPassword));
            user.setCreatedAt(LocalDateTime.now());
            user.setUpdatedAt(LocalDateTime.now());

            user.setStatus(1);
            userMapper.insert(user);

            log.info("手机号自动注册成功: userId={}, mobile={}", user.getId(), mobile);
        }

        // 4. 生成JWT令牌
        String token = jwtUtils.generateToken(user.getId(), user.getUsername());
        if (token == null) {
            log.error("生成JWT令牌失败: userId={}, mobile={}", user.getId(), mobile);
            throw new BusinessException("登录失败，请稍后再试");
        }

        // 5. 保存令牌到数据库
        saveUserToken(user.getId(), token, mobileLoginDTO.getDeviceType(),
                mobileLoginDTO.getDeviceInfo(), mobileLoginDTO.getLoginIp());

        // 6. 缓存用户令牌到Redis
        redisUtils.setUserToken(user.getId(), token);

        // 7. 组装用户信息
        UserDTO userDTO = BeanConverterUtils.convertToUserDTO(user, TOKEN_PREFIX + token);

        log.info("手机号登录成功: userId={}, mobile={}", user.getId(), mobile);
        return ResponseVo.success("登录成功", userDTO);
    }

    /**
     * 根据用户ID获取用户信息
     *
     * @param userId 用户ID
     * @return 用户信息
     */
    @Override
    public ResponseVo<UserDTO> getUserById(Long userId) {
        // 1. 查询用户信息
        User user = userMapper.selectById(userId);
        if (user == null || user.getStatus() != 1) {
            return ResponseVo.notFound();
        }

        // 2. 组装用户信息（不返回敏感信息）
        UserDTO userDTO = UserDTO.builder()
                .id(user.getId())
                .username(user.getUsername())
                .nickname(user.getNickname())
                .avatar(user.getAvatar())
                .createdAt(user.getCreatedAt())
                .mobile(user.getMobile())
                .email(user.getEmail())
                .status(user.getStatus())
                .build();

        return ResponseVo.success(userDTO);
    }

    /**
     * 根据用户ID获取用户公开信息
     * 不包含密码、邮箱等敏感信息
     *
     * @param userId 用户ID
     * @return 用户公开信息
     */
    @Override
    public ResponseVo<UserDTO> getUserPublicInfo(Long userId) {
        // 1. 查询用户信息
        User user = userMapper.selectById(userId);
        if (user == null || user.getStatus() != 1) {
            return ResponseVo.notFound();
        }

        // 2. 组装用户公开信息（仅包含非敏感信息）
        UserDTO userDTO = UserDTO.builder()
                .id(user.getId())
                .username(user.getUsername())
                .nickname(user.getNickname())
                .avatar(user.getAvatar())
                .createdAt(user.getCreatedAt())
                .build();

        return ResponseVo.success(userDTO);
    }

    /**
     * 更新用户信息（通过用户ID）
     *
     * @param updateDTO 更新信息
     * @param userId    用户ID
     * @return 更新结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseVo<Void> updateUser(UserUpdateDTO updateDTO, Long userId) {
        // 1. 查询用户信息
        User user = userMapper.selectById(userId);
        if (user == null || user.getStatus() != 1) {
            return ResponseVo.unauthorized();
        }

        // 2. 是否需要修改密码
        boolean passwordChanged = false;
        if (StringUtils.hasText(updateDTO.getOldPassword()) && StringUtils.hasText(updateDTO.getNewPassword())) {
            // 验证旧密码
            if (!passwordEncoder.matches(updateDTO.getOldPassword(), user.getPassword())) {
                return ResponseVo.paramError("原密码错误");
            }

            // 设置新密码
            user.setPassword(passwordEncoder.encode(updateDTO.getNewPassword()));
            passwordChanged = true;
        }

        // 3. 更新用户基本信息
        boolean profileUpdated = false;

        if (StringUtils.hasText(updateDTO.getNickname())) {
            user.setNickname(updateDTO.getNickname());
            profileUpdated = true;
        }

        if (StringUtils.hasText(updateDTO.getEmail())) {
            // 验证邮箱是否已被其他用户使用
            User existUser = userMapper.findByEmail(updateDTO.getEmail());
            if (existUser != null && !existUser.getId().equals(userId)) {
                return ResponseVo.paramError("邮箱已被使用");
            }

            user.setEmail(updateDTO.getEmail());
            profileUpdated = true;
        }

        if (StringUtils.hasText(updateDTO.getAvatar())) {
            user.setAvatar(updateDTO.getAvatar());
            profileUpdated = true;
        }

        user.setUpdatedAt(LocalDateTime.now());

        // 4. 保存用户信息
        if (profileUpdated || passwordChanged) {
            userMapper.updateById(user);
            log.info("用户信息更新成功: userId={}", userId);

            // 如果密码已更改，清除现有登录状态，需要重新登录
            if (passwordChanged) {
                redisUtils.deleteUserToken(userId);
                return ResponseVo.success("信息更新成功，请重新登录");
            }
        }

        return ResponseVo.success("信息更新成功");
    }

    /**
     * 用户注销（通过用户ID）
     *
     * @param userId 用户ID
     * @return 注销结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseVo<Void> logout(Long userId) {
        // 1. 查询用户信息
        User user = userMapper.selectById(userId);
        if (user == null) {
            return ResponseVo.unauthorized();
        }

        // 2. 使该用户的所有Token失效
        userTokenMapper.invalidateAllTokens(userId);

        // 3. 从Redis中删除用户Token
        redisUtils.deleteUserToken(userId);

        log.info("用户注销成功: userId={}", userId);
        return ResponseVo.success("注销成功");
    }

    /**
     * 验证用户Token是否有效
     *
     * @param userId 用户ID
     * @param token  Token内容
     * @return 是否有效
     */
    @Override
    public boolean validateToken(Long userId, String token) {
        log.info("验证用户Token: userId={}, token={}", userId, token);
        if (userId == null || StringUtils.isEmpty(token)) {
            return false;
        }

        // 先检查Redis中是否存在
        String cachedToken = redisUtils.getUserToken(userId);
        if (token.equals(cachedToken)) {
            return true;
        }

        // Redis不存在，从数据库检查
        LocalDateTime now = LocalDateTime.now();
        UserToken userToken = userTokenMapper.findValidToken(userId, token, now);
        if (userToken != null) {
            // 重新设置缓存
            redisUtils.setUserToken(userId, token);
            // 更新最后活跃时间
            userTokenMapper.updateLastActiveTime(userToken.getId(), now);
            return true;
        }

        return false;
    }

    /**
     * 保存用户Token到数据库
     */
    private void saveUserToken(Long userId, String token, String deviceType,
            String deviceInfo, String loginIp) {
        UserToken userToken = new UserToken();
        userToken.setUserId(userId);
        userToken.setToken(token);
        userToken.setDeviceType(StringUtils.hasText(deviceType) ? deviceType : "WEB");
        userToken.setDeviceInfo(deviceInfo);
        userToken.setLoginIp(loginIp);

        // 设置过期时间与JWT一致
        LocalDateTime now = LocalDateTime.now();
        userToken.setExpiredAt(now.plusSeconds(jwtExpiration));
        userToken.setLastActiveAt(now);
        userToken.setStatus(1);
        userToken.setCreatedAt(now);
        userToken.setUpdatedAt(now);

        userTokenMapper.insert(userToken);
        log.debug("用户Token保存成功: userId={}, tokenId={}", userId, userToken.getId());
    }

    /**
     * 发送验证码
     *
     * @param codeDTO 验证码请求
     * @return 发送结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseVo<Void> sendVerificationCode(VerificationCodeDTO codeDTO) {
        String mobile = codeDTO.getMobile();
        String type = codeDTO.getType();

        // 1. 检查发送频率限制
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime oneMinuteAgo = now.minusMinutes(1);
        int sendCount = verificationCodeMapper.countCodeSent(mobile, type, oneMinuteAgo, now);

        if (sendCount >= 1) {
            return ResponseVo.paramError("发送过于频繁，请稍后再试");
        }

        // 2. 检查注册状态（仅对注册验证码检查）
        if ("REGISTER".equals(type)) {
            Long existUserId = verificationCodeMapper.findUserIdByMobile(mobile);
            if (existUserId != null) {
                return ResponseVo.paramError("该手机号已注册");
            }
        }

        // 3. 通过极光短信API发送验证码
        JiGuangSmsService.SmsResult smsResult = jiGuangSmsService.sendVerificationCode(mobile, type);
        if (!smsResult.isSuccess()) {
            log.error("短信发送失败: {}", smsResult.getErrorMessage());
            return ResponseVo.error("短信发送失败: " + smsResult.getErrorMessage());
        }

        String msgId = smsResult.getMsgId();
        log.info("短信发送成功: mobile={}, msgId={}", mobile, msgId);

        // 4. 存储msgId到Redis方便快速查询
        redisUtils.storeMsgId(mobile, type, msgId);

        // 5. 记录到数据库（用于审计和统计）
        VerificationCode verification = new VerificationCode();
        verification.setMobile(mobile);
        verification.setType(type);
        verification.setMsgId(msgId);
        verification.setExpiredAt(now.plusMinutes(5)); // 5分钟有效期
        verification.setUsed(0);
        verification.setIp(null); // 实际项目中可从请求获取IP
        verification.setCreatedAt(now);
        verification.setUpdatedAt(now);

        verificationCodeMapper.insert(verification);

        return ResponseVo.success("验证码已发送");
    }

    /**
     * 生成随机密码
     */
    private String generateRandomPassword() {
        String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        StringBuilder password = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < 12; i++) {
            password.append(chars.charAt(random.nextInt(chars.length())));
        }
        return password.toString();
    }

    /**
     * 根据用户ID获取用户名
     *
     * @param userId 用户ID
     * @return 用户名
     */
    @Override
    public String getUsernameById(Long userId) {
        // 从数据库获取
        User user = userMapper.selectById(userId);
        return user != null ? user.getUsername() : "未知用户";
    }

    /**
     * 根据用户ID获取头像
     *
     * @param userId 用户ID
     * @return 头像URL
     */
    @Override
    public String getUserAvatarById(Long userId) {
        // 从数据库获取
        User user = userMapper.selectById(userId);
        return user != null ? user.getAvatar() : "";
    }
}